"""
provides an interface to load and draw Wavefront object files.
created by: Toby de Havilland
edited by: Erwan Martin (public@fzwte.net)
"""

from pyglet import gl, image
import os.path
import context
import groups


class CatalogueTextures : 
    def __init__(self):
        self.catalogue = {}

    def chargerTexture(self,nom):
        try:
            return self.catalogue[nom]
        except KeyError:
            tex_image = image.load(nom)
            print ">>>>> TEXTURE ", nom, " CHARGEE"
            texture = tex_image.get_texture()
            gl.glBindTexture(gl.GL_TEXTURE_2D, texture.id)
            gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
            gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
            gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE)
            self.catalogue[nom] = texture
            return texture

catalogueTextures = CatalogueTextures()

class Face(object):
    def __init__(self):
        self.triIndices = [] # repertorie les indices de chaque vertice, texture et normales
        self.materialName = ""

class ObjModel:
    def __init__(self):
        self.materials = {}
        self.faces = []
        self.vertex_list = []

    def LoadMTL(self, fileName):
        mtl_file = open(fileName, "r")
        
        try:
            for line in mtl_file:
                # Parse command and data from each line
                words = line.split()
                if len(words):
                    command = words[0]
                    data = words[1:]
                    
                    if command == 'newmtl':
                        try:
                            material = data[0]
                            
                        except IndexError:
                            return
                    
                    elif command == 'map_Kd':
                        modelPath = os.path.split(fileName)[0]
                        mat_path = os.path.join(modelPath, data[0])
                        self.materials[material] = catalogueTextures.chargerTexture(mat_path)

        finally:
            mtl_file.close()

    def load(self, transform, fileName):
        """
        load Obj Files
        """
        currentface = None 
        vertices = []
        texCoords = []
        normals = []
        
        def face_command(data):
            """
            Parsing f command in Obj
            input example -> ['78/-58/58', '69//50', '79/59/59']
            output -> currentface.triIndices = [(77, -57+len(vt), 57), (68, 0, 49), (78, 58, 58)]
            """
            
            if len(data) == 4:
                face_command(data[:3])
                face_command(data[2:3] + data[:1])
            elif len(data) == 3:                
                # Parse indices from triples
                for word in data:
                    indicies = [int(b) - 1 if b else 0 for b in word.split('/')]
                    indicies += [0] * (3 - len(indicies)) # wtf ? ca ne change rien, meme pour un negatif ou un manquant
                    
                    vi, ti, ni = indicies
                    # Make relative negative vert indicies absolute
                    # un index -2 sur une longeur de 10 va indexer le 9e elmt et pas le 1er, normal?
                    while vi < 0:
                        vi += len(vertices) + 1

                    while ti < 0:
                        ti += len(texCoords) + 1
                        
                    while ni < 0:
                        ni += len(normals) + 1 # changed ti to ni

                    currentface.triIndices.append((vi, ti, ni))
        
        obj_file = open(fileName)
        #print("opening %s" % fileName)
        
        try:
            for line in obj_file:
                # Parse command and data from each line
                words = line.split()
                if len(words):
                    command = words[0]
                    data = words[1:]
        
                    if command == 'mtllib': # Material library
                        modelPath = os.path.dirname(fileName)
                        mtllibPath = os.path.join(modelPath, data[0])
                        self.LoadMTL(mtllibPath)
        
                    elif command == 'v': # Vertex
                        x, y, z = data
                        vertex = (float(x), float(y), float(z))
                        vertices.append(vertex)
        
                    elif command == 'vt': # Texture coordinate
                        s, t = data[:2]
                        texCoord = (float(s), float(t))
                        texCoords.append(texCoord)
        
                    elif command == 'vn': # Normal
                        x, y, z = data
                        normal = (float(x), float(y), float(z))
                        normals.append(normal)
                    
                    elif command == 'usemtl' : # Use material
                        currentface = Face() 
                        currentface.tex = self.materials[data[0]]
                        self.faces.append(currentface)
                    
                    elif command == 'f':
                        face_command(data)
        finally:
            obj_file.close()
        
        def obj2gl(face):
            vert = []
            tex = []
            norm = []
            
            for vi, ti, ni in face.triIndices:
                vert += vertices[vi]
                tex += texCoords[ti]
                norm += normals[ni]
                
            return vert, tex, norm

        self.transform = transform
        for face in self.faces:
            vert, tex, norm = obj2gl(face)
            
            vlist = context._context._batch.add(len(face.triIndices), gl.GL_TRIANGLES,
                                        groups.TransformGroup(groups.TextureBindGroup(face.tex), transform),
                                    ("v3f/static", vert),
                                    ("t2f/static", tex),
                                    ("n3f/static", norm))
            
            self.vertex_list.append(vlist)
                

        print("Model loaded")
        print("Number vertices: %d" % len(vertices))
        print("Number texCoords: %d" % len(texCoords))
        print("Number normals: %d" % len(normals))
        print("Number materials: %d" % len(self.materials))
        print("Number faces: %d" % len(self.faces))
        
    def delete(self):
        for vlist in self.vertex_list:
            vlist.delete()

